﻿; (function() {
    if (!window.$axpin) window.$axpin = {};

    /**
     * Preps the content path and loads the stylesheet for the pin plugin
     **/
    $(document).ready(function () {
        $('<link>').appendTo('head').attr({
            rel: 'stylesheet',
            type: 'text/css',
            href: '/Content/plugins/axPin/styles/axPin9.css'
        });

        // Load in resize listener lib
        $axure.utils.loadJS('/Scripts/plugins/feedback/axResizeListener.js');
    });

    /**
     * Array of the possible pin colors. Just to keep track as the index corresponds to the class
     **/
     $axpin.pinColor = [
        "PURPLE",   // .pinColor_0
        "BLUE",     // .pinColor_1
        "YELLOW",   // .pinColor_2
        "RED",      // .pinColor_3
        "BLACK",    // .pinColor_4
        "GREY"      // .pinColor_5
    ];

    /**
     * Basic enum just to determine location of speech bubble tail
     **/
    $axpin.speechPosition = {
        TOP: 0,
        BOTTOM: 1,
        RIGHT_TOP: 2,
        RIGHT_BOTTOM: 3,
        LEFT_TOP: 4,
        LEFT_BOTTOM: 5
    };

    $axpin.pinZindex = 1;
    var pinColorIndex = 0;

    var $clippingBounds = null;
    var $targetPanel = null;
    var $clearOnClickObjs = null;

    var $targetPinToMove = null;
    var prevMousePos = {
        X: 0,
        Y: 0
    };

    $axpin.suppressClickPin = null;

    //-----------------------------------------------------------------------
    // Utility
    //-----------------------------------------------------------------------

    $axpin.axPinJsAvailable = true;

    /**
     * Remove the add pin panel and show any previously expanded pins
     **/
    $axpin.removeAll = function () {
        $('#addPinPanel').remove();
        $('.pinControlOverlay').remove();

        // Find all saved pins with a certain class to be toggled to be visible
        $('.pinWasExpanded').removeClass('pinWasExpanded').click();
        $axure.messageCenter.postMessage('addPinModeToggle', false);
    }

    /**
     * Determines what kind of speech bubble to use and it's position in respect to it's pin
     **/
    $axpin.getPinBubblePositioning = function(tailPosition) {
        var results = {
            pinClass: "",
            bubbleTop: 0,
            bubbleLeft: 0,
            bubbleRight: 0
        };

        switch (tailPosition) {
        case $axpin.speechPosition.RIGHT_TOP:
        case $axpin.speechPosition.RIGHT_BOTTOM:
            results.bubbleTop = -9;
            results.bubbleRight = 47;
            results.pinClass = "pinTailLeft";
            break;
        default:
        case $axpin.speechPosition.LEFT_TOP:
        case $axpin.speechPosition.LEFT_BOTTOM:
            results.bubbleTop = -9;
            results.bubbleLeft = 45;
            results.pinClass = "pinTailRight";
            break;
        }

        return results;
    };

    // Determines if browser is Mozilla Firefox
    function isFirefox() {
        return typeof InstallTrigger !== 'undefined';
    };

    // Determines if browser is mobile
    function isMobile() {
        var userAgent = navigator.userAgent || navigator.vendor || window.opera;

        return (/window phone/i.test(userAgent)) ||
            (/android/i.test(userAgent)) ||
            (/iPad|iPhone|iPod/.test(userAgent) && !window.MSStream);
    };

    // Determines if device is iOS
    function isIOS() {
        var userAgent = navigator.userAgent;

        return userAgent.match(/iPhone/i) || userAgent.match(/iPad/i) || userAgent.match(/iPod/i);
    };

    // Get width of sitemapHost if it is currently visible
    function determineSitemapHostWidth() {
        return $('#sitemapHost').is(':visible') ? parseInt($('#sitemapHost').css('width')) : 0;
    }

    /**
     * Get the dimensions (width and height) of the topPanel
     **/
    $axpin.determineTopPanelDimensions = function() {
        $topPanel = $('#topPanel');

        if ($topPanel.length) {
            return {
                width: $topPanel.width(),
                height: $topPanel.outerHeight()
            };
        } else {
            return {
                width: 0,
                height: 0
            };
        }
    };

    /**
     * Get the dimensions (width and height) of the iframe content window
     * 
     * This is the full width and height of the prototype (beyond the window width and height)
     **/
    $axpin.determineIframeDimensions = function() {
        var $iframe = $($targetPanel.find('iframe')[0].contentWindow);

        var scale = $scalePanel.css('transform');
        scale = (scale == "none") ? 1 : Number(scale.substring(scale.indexOf('(') + 1, scale.indexOf(',')));

        return {
            width: $iframe.width() * scale,
            height: $iframe.height() * scale
        };
    };

    /**
     * Get the left and top of the iframe in respect to the target panel.
     * Position of this (upper left hand corner) should match the existingPinPanel position
     * 
     * This is intended to get when prototypes are center aligned or not
     **/
    $axpin.determineIframePosition = function () {
        var dimensions = $axpin.determineIframeDimensions();
        var $iframe = $($targetPanel.find('iframe')[0].contentWindow);

        var $body = $($iframe[0].document.body);
        var bodyWidth = $body.offset().left !== 0  ? $body.width() : Math.max(dimensions.width, $body.width());

        if(isFirefox()) {
            var left = $body[0].getBoundingClientRect().left;

            bodyWidth = left !== 0 ? $body.width() : Math.max(dimensions.width, $body.width());
        }

        var scale = $scalePanel.css('transform');
        scale = (scale == "none") ? 1 : Number(scale.substring(scale.indexOf('(') + 1, scale.indexOf(',')));
        bodyWidth *= scale;

        return {
            top: 0,// Math.max(0, (dimensions.height - $($iframe[0].document.body).height()) / 2),
            left: Math.max(0, (dimensions.width - bodyWidth) / 2)
        };
    };

    /**
     * Get the scrollLeft and scrollTop of the iframe document
     **/
    $axpin.determineIframeScroll = function () {
        //var $iframe = $($targetPanel.find('iframe')[0].contentWindow.document);
        //var $iframe = $($targetPanel.find('iframe')[0].contentDocument);
        if (IOS) {
            return {
                scrollTop: -parseInt($clippingBounds.css('top')),
                scrollLeft: -parseInt($clippingBounds.css('left'))
            };
        } else {
            var $iframe = $($targetPanel.find('iframe')[0].contentWindow);

            var scale = $scalePanel.css('transform');
            scale = (scale == "none") ? 1 : Number(scale.substring(scale.indexOf('(') + 1, scale.indexOf(',')));

            return {
                scrollTop: $iframe.scrollTop() * scale,
                scrollLeft: $iframe.scrollLeft() * scale
            };
        }
    };

    // Manually shifts the bubble up and down if it impedes on the bottom viewport boundary
    function shiftBubbleIfPastViewport($pin) {        
        var visible = checkIfPinWithinViewport($pin);
        if (!visible) {
            var pinPosition = $pin.position();
            var pinContainerPosition = $clippingBounds.position();           
            var viewportDimensions = {
                width: $targetPanel.width(),
                height: $targetPanel.height()
            }

            var isNewPin = $pin.find('.pinIssueBubble, .newPinIssueBubble').hasClass("newPinIssueBubble");
            if (isNewPin) {
                var $pinBorder = $('.pinBorderOverlay');
                pinContainerPosition = {
                    top: parseInt($pinBorder.css("border-top-width"), 10),
                    left: parseInt($pinBorder.css("border-left-width"), 10),
                }
                viewportDimensions.width = $pinBorder.width();
                viewportDimensions.height = $pinBorder.height();
            }

            var positionWithinViewport = {
                left: pinPosition.left + pinContainerPosition.left,
                top: pinPosition.top + pinContainerPosition.top
            };

            var $pinIssueBubble = $pin.find(".pinIssueBubble, .newPinIssueBubble");
            var resultLeft = -positionWithinViewport.left + (viewportDimensions.width - $pinIssueBubble.width()) / 2;
            var resultTop = -positionWithinViewport.top + (viewportDimensions.height - $pinIssueBubble.height()) / 2;

            $pin.removeClass("pinTailLeft").removeClass("pinTailRight");
            $pinIssueBubble.css('left', resultLeft + 'px');
            $pinIssueBubble.css('top', resultTop + 'px');
            return;
        }
            
        var defaultBubbleShift = 9;
        
        var $pinIssueBubble = $pin.find('.pinIssueBubble, .newPinIssueBubble');
        var pinPosition = $pin.position();
        var bubbleHeight = $pinIssueBubble.outerHeight(true);

        var bubbleTop = pinPosition.top - defaultBubbleShift - 10; // -10 px for top margin
        var viewportTop = 0;

        var isNewPin = $pinIssueBubble.hasClass('newPinIssueBubble');

        if (!isNewPin) {
            var clippingBoundsPosition = $clippingBounds.position();
            viewportTop -= clippingBoundsPosition.top;
        } else {
            viewportTop -= parseInt($('.pinBorderOverlay').css("border-top-width"), 10);
        }
        
        if (bubbleTop <= viewportTop) {
            var shift = viewportTop - bubbleTop - defaultBubbleShift;            
            if (shift > 0) {
                shift = 0;
            }
            $pinIssueBubble.css('top', shift + 'px');
        }
        
        var bubbleBottom = pinPosition.top + bubbleHeight - defaultBubbleShift + 10; // +10 px for bottom margin
        var viewportBottom = viewportTop + $targetPanel.height();

        if (isNewPin) {
            viewportBottom -= parseInt($('.pinBorderOverlay').css("border-bottom-width"), 10)
        }

        if (bubbleBottom >= viewportBottom) {
            var shift = bubbleBottom - viewportBottom + defaultBubbleShift;
            if (shift > bubbleHeight - $pin.height()) {
                shift = bubbleHeight - $pin.height();
            }
            $pinIssueBubble.css('top', -shift + 'px');
        }
    };

    function checkIfPinWithinViewport($pin) {
        var position = $pin.position();        
        var pinContainerPosition = $clippingBounds.position();

        var viewportDimensions = {
            width: $targetPanel.width(),
            height: $targetPanel.height()
        }

        var isNewPin = $pin.find('.pinIssueBubble, .newPinIssueBubble').hasClass("newPinIssueBubble");
        if (isNewPin) {
            var $pinBorder = $('.pinBorderOverlay');
            pinContainerPosition = {
                top: parseInt($pinBorder.css("border-top-width"), 10),
                left: parseInt($pinBorder.css("border-left-width"), 10),
            }
            viewportDimensions.width = $pinBorder.width();
            viewportDimensions.height = $pinBorder.height();
        }
    
        var positionWithinViewport = {
            left: position.left + pinContainerPosition.left,
            top: position.top + pinContainerPosition.top
        };
    
        var elementTop = positionWithinViewport.top;
        var elementBottom = elementTop + $pin.height();
        var elementLeft = positionWithinViewport.left;
        var elementRight = elementLeft + $pin.width();
        
        staticContainerPosition = {
            left: isNewPin ? pinContainerPosition.left : 0,
            top: isNewPin ? pinContainerPosition.top : 0
        }
        var top = staticContainerPosition.top;
        var bottom = top + viewportDimensions.height;
        var left = staticContainerPosition.left;
        var right = left + viewportDimensions.width;
        
        return !((top >= elementBottom) || (bottom <= elementTop) || (left >= elementRight) || (right <= elementLeft));
    }

    // Updates the speech bubble for a given pin's orientation and shift.
    function updateSpeechBubbleOrientationAndShift($pin) {
        var $pinIssueBubble = $pin.find('.pinIssueBubble, .newPinIssueBubble');
        if(!$pinIssueBubble.is(':visible')) {
            return;
        }

        // Remove fixed position        
        $pinIssueBubble.css("position","");
        
        //  Redetermine best position for the pin bubble
        var pinPosition = $pin.position();
        var isNewPin = $pinIssueBubble.hasClass("newPinIssueBubble");
        var updatedPosition = $axpin.getPinBubblePositioning(determinePinBubbleOrientation(pinPosition.left, pinPosition.top, isNewPin));

        $pin.removeClass('pinTailRight pinTailLeft').addClass(updatedPosition.pinClass);
        $pinIssueBubble.css('top', updatedPosition.bubbleTop + 'px');
        if (updatedPosition.bubbleLeft) {
            $pinIssueBubble.css('left', updatedPosition.bubbleLeft + 'px');
            $pinIssueBubble.css('right', '');
        } else {
            $pinIssueBubble.css('left', '');
            $pinIssueBubble.css('right', updatedPosition.bubbleRight + 'px');
        }

        // Shifts bubble up if bubble exceeds the bottom of the viewport
        shiftBubbleIfPastViewport($pin);
    };


    //-----------------------------------------------------------------------
    // Public Functions 
    //-----------------------------------------------------------------------

    $axpin.updateSpeechBubbleOrientationAndShift = updateSpeechBubbleOrientationAndShift;

    /**
     * axpin initialization
     **/
    $axpin.init = function ($bounds, $tPanel, $sPanel, $clearOnClickObjects) {
        // Store related objects
        $clippingBounds = $bounds;
        $targetPanel = $tPanel;
        $scalePanel = $sPanel;
        $clearOnClickObjs = $clearOnClickObjects;

        // Resize listener
        window.addEventListener('resize', function (event) {
            if(isMobile()) {
                return;
            } 

            $axpin.removeAll();
        }, false);

        // Mobile specific event handler to remove all when the device is roatated
        if(isMobile()) {
            $(window).bind('orientationchange', function(event) {
                $axpin.removeAll();
            });
        }

        // Escape key event handler
        $(document).keydown(function (e) {
            if (e.keyCode === 27) {      // esc key
                //e.stopPropagation();

                $axpin.removeAll();
            }
        });

        // Pin moving event handler
        if(!isMobile()) {
            $(document).mousemove(function(e) {
                if($targetPinToMove != null) {
                    e.preventDefault();

                    var diffX = e.clientX - prevMousePos.X;
                    var diffY = e.clientY - prevMousePos.Y;
                    prevMousePos.X = e.clientX;
                    prevMousePos.Y = e.clientY;

                    $targetPinToMove.css('left', parseInt($targetPinToMove.css('left')) + diffX + 'px');
                    $targetPinToMove.css('top', parseInt($targetPinToMove.css('top')) + diffY + 'px');

                    updateSpeechBubbleOrientationAndShift($targetPinToMove);
                }
            });
        } else {
            document.addEventListener('touchmove', function(e) {
                if($targetPinToMove != null) {
                    e.preventDefault();

                    var touch = e.touches[0] != null ? e.touches[0] : e.originalEvent.touches[0];
                    var diffX = touch.clientX - prevMousePos.X;
                    var diffY = touch.clientY - prevMousePos.Y;
                    prevMousePos.X = touch.clientX;
                    prevMousePos.Y = touch.clientY;

                    $targetPinToMove.css('left', parseInt($targetPinToMove.css('left')) + diffX + 'px');
                    $targetPinToMove.css('top', parseInt($targetPinToMove.css('top')) + diffY + 'px');

                    updateSpeechBubbleOrientationAndShift($targetPinToMove);
                }
            });
        }

        // Window fully loaded event handler
        $(window).on("load", function () {
            window.loaded = true;

            // Listener: Removes screenshot/preview panel if present
            $targetPanel.resize(function () {
                if ($('#addPinPanel').length) {
                    $axpin.removeAll();
                }

                // Specifically for when minimize and maximize is pressed. This shows the pin again
                //if($('#leftPanel').is(':visible') && $('a[pluginid="feedbackHost"]').hasClass('selected') && !$('#existingPinsOverlay').is(':visible')) {
                //    $axpin.showPins(true, true);
                //}
            });

            // If any other tabs are hit (or the minimize) hide all pins
            //$("a[pluginid='sitemapHost'], a[pluginid='pageNotesHost'], a[pluginid='debugHost'], #interfaceControlFrameMinimizeButton").click(function () {
            //    $axpin.showPins(false, true);
            //});

            // Hide pins if the feedbackHost button is pressed, otherwise show (might change this as we want to reload all issues)
            //$('a[pluginid="feedbackHost"]').click(function() {
            //    if ($(this).hasClass('selected')) {
            //        $axpin.showPins(true, true);
            //    } else {
            //        $axpin.showPins(false, true);
            //    }
            //});
        });

        // Load the existing pin panel
        (function iframeLoaded() {
            var iframe = $targetPanel.find('iframe')[0];
            var iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
            var isExpo = window.$axure && $axure.expo;

            // iframeDoc URL should always be URL for project page or about:blank (empty page) when first loading a project from start.html
            if((iframeDoc.readyState == 'complete' && iframeDoc.URL != "about:blank") || isExpo) {
                createPinPanel(); // Need to wait til iframe is fully loaded
            } else {
                setTimeout(iframeLoaded, 100);
            }
        })();

        // Just once, set the #rightPanel to be hidden (so that pins when scrolled left get hidden) - ONLY FOR ALL CASES BUT IOS
        if(!isIOS()) {
            $targetPanel.css('overflow', 'hidden');
        }
    };

    /**
     * Removes the existing pin panel and existing pins container and rebuild it
     **/
    $axpin.prepareExistingPins = function () {
        $('#existingPinsOverlay').remove();

        createPinPanel();
    };

    /**
     * Show or hide the pin panel which dispalys all the pins
     * 
     * @param {True to show pins, false otherwise} bool 
     **/
    $axpin.showPins = function(bool, includeAddPanel, triggerPositionUpdateForActivePin) {
        if(bool) {
            $('#existingPinsOverlay').show();
            //$('#existingPinsOverlay').css("overflow","visible");
            if(includeAddPanel) {
                $('#addPinPanel').show();
            }
        } else {     
            $('#existingPinsOverlay').hide();
            //$('#existingPinsOverlay').css("overflow", "hidden");

            if (includeAddPanel) {
                $('#addPinPanel').hide();
            }
        }

        // Manually trigger if any open pins (should only be one) to manually reposition if necessary
        if(triggerPositionUpdateForActivePin) {
            updateSpeechBubbleOrientationAndShift($('.expandedPin'));
        }
    }

    $axpin.closeExpandedPins = function () {
        var $expandedPin = $('.expandedPin');
        if ($expandedPin.length) {
            $expandedPin.click();
        }
    }

    /**
     * Hides the temporary pin and shows the newly saved pin
     **/
    $axpin.publishPin = function ($pin, tagId, issueCode) {
        // Remove the temporary pin and expand permanent pin
        $('.pinWasExpanded').removeClass('pinWasExpanded');
        $pin.hide();
        
        var $newPin = $('#pinId_' + issueCode);      
        $newPin.click();          
        $newPin.find('.pinReplyContainer').hide();
        // update position after change pin height
        updateSpeechBubbleOrientationAndShift($newPin);
        setTimeout(function() {
            $newPin.removeClass("expandedPin");        
            $newPin.find(".pinIssueBubble, :before").fadeOut(1000);
        }, 500)
    };

    /**
     * Given pin issues, generates and binds the pins to the existing pin panel
     **/
    $axpin.loadPin = function (issue, profiles, isResolved, htmlGen, eventBind) {
        var scale = 1;
        if (!$axure.expo){
            // HTML VIEWER
            var scale = $scalePanel.css('transform');
            scale = (scale == "none") ? 1 : Number(scale.substring(scale.indexOf('(') + 1, scale.indexOf(',')));
        } else if ($axure.player && $axure.player.isInspect){
            // IMAGE VIEWER
          scale = $axure.page.scale || 1;
        } else if ($axure.player && !$axure.player.isInspect && window.$axExpo) {
            // VUE VIEWER
            const getViewportPosition = window.$axExpo.getViewportPosition;
            if (typeof(getViewportPosition) === "function"){
                const viewport = getViewportPosition();
                scale = viewport ? viewport.viewportScale : 1;
            }
        }

        var initialPosition = {
            x: issue.x,
            y: issue.y
        }

        issue.x = issue.x * scale;
        issue.y = issue.y * scale;

        $('#existingPinsOverlay').append(htmlGen(issue, profiles, isResolved, determinePinBubbleOrientation(issue.x, issue.y, false)));

        var $pin = $('#pinId_' + issue.code);

        var width = $pin.width() || 0;
        var height = $pin.height() || 0;
        $pin.css({
            "left": issue.x - (width / 2),
            "top": issue.y - (height / 2),
        })
        
        $pin.attr({
            "data-x": initialPosition.x,
            "data-y": initialPosition.y,
        })
        
        eventBind($pin);

        // Bubble resize listener: Repositions the speech bubble if it exceeds the view window
        addResizeListener($pin.find('.pinIssueBubble')[0], function () {
            shiftBubbleIfPastViewport($pin);
        });

        // Binding for pin click. Performs relocation of pin bubble when shown
        $('#pinId_' + issue.code).click(function(e) {
             // Ignore children elements
             if (e.target !== $pin[0]) {
                return;
            }
            updateSpeechBubbleOrientationAndShift($(this));
        });
    };

    /**
     * Get or Set the current pin color. Will return the class to be used to match the color.
     **/
    $axpin.getOrSetCurrentPinColorClass = function (index) {
        // Set current default color
        if(index != null) {
            try {
                var colorIndex = parseInt(index);
                if(index >= 0 && index < $axpin.pinColor.length) {
                    pinColorIndex = colorIndex;
                } else {
                    pinColorIndex = 0;
                }
            } catch(e) { }
        }

        return "pinColor_" + pinColorIndex;
    };

    /**
     * Get the index (which represents the color) of the current pin color
     **/
    $axpin.getCurrentPinColorIndex = function() {
        return pinColorIndex;
    }

    /**
     * Build the class that represents the color for a given index
     **/
    $axpin.getPinColorClassFromIndex = function(index) {
        return "pinColor_" + (index >= 0 && index < $axpin.pinColor.length ? index : 0);
    };

    /**
     * Given a pincolor_ class, get the value aka the index (mainly used to be stored when we have the class name)
     **/
    $axpin.getPinColorIndexFromClass = function(pinColor) {
        return parseInt(pinColor.replace('pinColor_', ''));
    }

    /**
     * Given a jquery object, gets the classes and identifies the pinColor_ class used.
     **/
    $axpin.getPinColorFromObject = function($obj) {
        if (!$obj || !$obj.length) return;
        var classes = $obj.attr('class').split(' ');
        var result = null;

        $.each(classes, function(index, value) {
            if(value.indexOf('pinColor_') > -1) {
                result = value;
            }
        });

        return result;
    }

    /**
     * Given an id of pin, gets the classes and identifies the pinColor_ class used.
     **/
    $axpin.getPinColorFromPinId = function(pinId) {
        if (!pinId) return;
        var classes = $('#pinId_' + pinId).attr('class').split(' ');
        var result = null;

        $.each(classes, function(index, value) {
            if(value.indexOf('pinColor_') > -1) {
                result = value;
            }
        });

        return result;
    }

    /**
     * Sets the pin that is to be relocated on the existing pin panel
     * 
     * @param leftOffset - the sidepanel widths (#leftPanel + .vsplitbar)
     **/
    $axpin.setMovingPin = function($pin, mEvent, isTouch) {
        $targetPinToMove = $pin;

        if($pin == null) {
            prevMousePos.X = 0;
            prevMousePos.Y = 0;
        } else {
            if (mEvent != null) {
                prevMousePos.X = isTouch ? mEvent.touches[0].clientX : mEvent.clientX;
                prevMousePos.Y = isTouch ? mEvent.touches[0].clientY : mEvent.clientY;
            }
        }
    }

    //-----------------------------------------------------------------------
    // Events
    //-----------------------------------------------------------------------
 
    // Scrolls the iframe to the designated left and top values
    function scrollIframe(scrollEvent, scrollLeft, scrollTop) {
        var $iframe = $($targetPanel.find('iframe')[0].contentWindow.document.body);

        $iframe.scrollTop(scrollTop);
        $iframe.scrollLeft(scrollLeft);
    };    

    // Given the x and y as well as which pin panel is present, this determines where the "tail" and position is for 
    // the speech bubble around the pin
    function determinePinBubbleOrientation(x, y, isNewPin) {        
        var pinWidth = 36;
        
        var targetPanelDimensions = {
            width: $targetPanel.width(),
            height: $targetPanel.height()
        };

        var clippingBoundsPosition = $clippingBounds.position();

        var scale = $scalePanel.css('transform');
        scale = (!scale || scale == "none") ? 1 : Number(scale.substring(scale.indexOf('(') + 1, scale.indexOf(',')));

        var pinPositionOnPanel = {
            left: pinWidth * scale + (isNewPin ? x : x + clippingBoundsPosition.left),
            top: pinWidth * scale + (isNewPin ? y : y + clippingBoundsPosition.top)
        }

        var speechPosition;

        var isRight = pinPositionOnPanel.left > targetPanelDimensions.width / 2;
        var isTop = pinPositionOnPanel.top < targetPanelDimensions.height / 2;

        if(isTop && isRight) {
            speechPosition = $axpin.speechPosition.RIGHT_TOP;
        } else if(!isTop && isRight) {
            speechPosition = $axpin.speechPosition.RIGHT_BOTTOM;
        } else if(!isTop && !isRight) {
            speechPosition = $axpin.speechPosition.LEFT_BOTTOM;
        } else {
            speechPosition = $axpin.speechPosition.LEFT_TOP;
        }

        return speechPosition;
    };
    // Performs the actual html and event binding for the new temporary pin. Firgures out the position it will sit due to borders.
    function drawPin(x, y, color, count, htmlGen, eventBind) {
        var shiftedX = x - 27;      // (36 / 2) + 9
        var shiftedY = y - 27;

        // Remove any existing pins that aren't permanent (one one working pin at a time)
        $('.pinTargetOverlay').find('.pin').remove();

        var orientation = determinePinBubbleOrientation(shiftedX, shiftedY, true);
        var positioning = $axpin.getPinBubblePositioning(orientation);
        var newPin = [
            '<div id="tempPinId_', count, '" class="pin expandedPin pinColor_', color, ' ',positioning.pinClass, '" style="left: ', shiftedX, 'px; top: ', shiftedY, 'px; z-index:', ++$axpin.pinZindex, ';">',
                "",
                htmlGen(orientation, color, count),
            '</div>'
        ].join("");

        $('.pinTargetOverlay').append(newPin);

        var $pin = $('.pinTargetOverlay').find('#tempPinId_' + count);
        shiftBubbleIfPastViewport($pin);

        eventBind($pin.find('.newPinIssueContainer'));
        $pin.click(function() {
            updateSpeechBubbleOrientationAndShift($(this));
        });

        // Listens to when the window resizes itself (doesn't work when adding screenshots!)
        addResizeListener($pin.find('.axPinContextSpeechBubble')[0], function () {
            shiftBubbleIfPastViewport($pin);
        });
    };

    // Appends the new pin issue on the temporary pin panel. This determines where the pin will sit as well as gen the bubble and events
    function addNewPinComment(clickEvent, scrollLeft, scrollTop, htmlGen, eventBind) {
        //var leftOffset = parseInt($targetPanel.css('left'));
        var leftOffset = determineSitemapHostWidth();
        var x = clickEvent.pageX - leftOffset + scrollLeft;
        var y = clickEvent.pageY + scrollTop;

        drawPin(x, y, $axpin.getCurrentPinColorIndex(), 0, htmlGen, eventBind);
    };


    //-----------------------------------------------------------------------
    // HTML Related Functionality
    //-----------------------------------------------------------------------

    // Creates the panel and overlay that will display saved pins
    function createPinPanel() {
        if (!$('#existingPinsOverlay').length) {
            var existingPinsOverlay = document.createElement('div');
            existingPinsOverlay.setAttribute('id', 'existingPinsOverlay');
            
            $clippingBounds.prepend(existingPinsOverlay);
            //$targetPanel.prepend(existingPinsOverlay);
          
            $(existingPinsOverlay).append('&nbsp;');

            //bindPinPanelEvents();
        }
    };

    // Adds the overlay over the control panel which disables any events on it. 
    function addPinControlOverlay($addPinPanel, $anchor) {
        if ($('#addPinPanel').length && !$('.pinControlOverlay').length) {
            // controlPanel dimensions
            var width = parseInt($('#feedbackHost').css('width'));
            var height = parseInt($('#feedbackHost').css('height'));

            // Build pin control overlay
            var pinControlOverlay = document.createElement('div');
            pinControlOverlay.setAttribute('class', 'pinControlOverlay');
            pinControlOverlay.style.right = determineSitemapHostWidth() + 'px';
            pinControlOverlay.style.width = width.toString() + 'px';
            pinControlOverlay.style.height = height.toString() + 'px';
            pinControlOverlay.style.zIndex = 200;

            var $topPanel = $('#topPanel');
            var anchorPos = ($anchor[0]).getBoundingClientRect();
            
            var topPosition = anchorPos.top;
            // subtract topPanel height only when it's visible
            if ($topPanel.is(':visible')) {
                topPosition -= $axpin.determineTopPanelDimensions().height;
            }
            var pinControlCloseBtn = [
                '<div id="feedbackNewIssuePinCloseBtn" class="feedbackPinBtn" style="top: ', topPosition, 'px; width: ', width - 20, 'px;">', // width - 20 = 10 px spaces on each side
                '   <span>Close</span>',
                '</div>'
            ].join("");

            //if (y != null) {
            //    previewPanel.style.top = y.toString() + 'px';
            //}

            $addPinPanel.append(pinControlOverlay);
            $(pinControlOverlay).append(pinControlCloseBtn);

            // Control Overlay for topPanel
            if ($topPanel.is(':visible')) {
                var dimensions = $axpin.determineTopPanelDimensions();

                // Build pin control overlay
                var pinControlOverlay = document.createElement('div');
                pinControlOverlay.setAttribute('class', 'pinControlOverlay');
                pinControlOverlay.style.top = '0px';
                pinControlOverlay.style.width = dimensions.width.toString() + 'px';
                pinControlOverlay.style.height = dimensions.height.toString() + 'px';
                pinControlOverlay.style.zIndex = 200;

                $topPanel.append(pinControlOverlay);
            }

            // Control Overlay for sitemap
            var $sitemap = $('#sitemapHost');
            if ($sitemap.is(':visible')) {
                var width = parseInt($sitemap.css('width'));
                var height = parseInt($sitemap.css('height'));

                // Build pin control overlay
                var pinControlOverlay = document.createElement('div');
                pinControlOverlay.setAttribute('class', 'pinControlOverlay');
                pinControlOverlay.style.left = -width + 'px';
                pinControlOverlay.style.width = width + 'px';
                pinControlOverlay.style.height = height.toString() + 'px';
                pinControlOverlay.style.zIndex = 200;

                $addPinPanel.append(pinControlOverlay);
            }
        }
    };

    // Adds the overlay where users will be adding pins
    function addPinTargetOverlay ($addPinPanel) {
        if ($('#addPinPanel').length && !$('.pinTargetOverlay').length) {
            var $iframe = $targetPanel.find('iframe');
            var iframeScrollTop = $($iframe[0].contentWindow.document.body).scrollTop();
            var iframeScrollLeft = $($iframe[0].contentWindow.document.body).scrollLeft();

            // 
            var x = parseInt($targetPanel.css('left'));
            var width = parseInt($targetPanel.css('width'));
            var height = parseInt($targetPanel.css('height'));

            var pinBorderOverlay = document.createElement('div');
            pinBorderOverlay.setAttribute('class', 'pinBorderOverlay');
            pinBorderOverlay.style.left = x.toString() + 'px';
            pinBorderOverlay.style.width = (width - 18).toString() + 'px';     // subtract 18 due to border, 9 each for left and right side
            pinBorderOverlay.style.height = (height - 18).toString() + 'px';   // subtract 18 due to border, 9 each for left and right side

            var pinTargetOverlay = document.createElement('div');
            pinTargetOverlay.setAttribute('class', 'pinTargetOverlay');

            $addPinPanel.append(pinBorderOverlay);

            $(pinBorderOverlay).append(pinTargetOverlay);
            $(pinTargetOverlay).append('&nbsp;');

            $(pinBorderOverlay).scrollTop(iframeScrollTop);
            $(pinBorderOverlay).scrollLeft(iframeScrollLeft);
        }
    };

    // Binds events for when the add pin panels and overlays are present
    function bindAddPinPanelEvents(htmlGenerator, eventBind) {
        $('.pinTargetOverlay').click(function (event) {
            if ($('.pinTargetOverlay').is(event.target)) {
                // Hide opened issue bobble if it's visible
                if ($('#existingPinsOverlay .pinIssueBubble').is(':visible')) {
                    $('#existingPinsOverlay .expandedPin').removeClass('expandedPin');
                    $('#existingPinsOverlay .pinIssueBubble').hide();
                }

                var scrollTop = $(this).parent().scrollTop();
                var $topPanel = $('#topPanel');
                // subtract topPanel height only when it's visible
                if ($topPanel.is(':visible')) {
                    scrollTop -= $axpin.determineTopPanelDimensions().height;
                }
                addNewPinComment(event, $(this).parent().scrollLeft(), scrollTop, htmlGenerator, eventBind);
            }
        });

        $('.pinControlOverlay').click(function () {
            $axpin.removeAll();
        });

        $('.pinBorderOverlay').scroll(function(event) {
            scrollIframe(event, $(this).scrollLeft(), $(this).scrollTop());
        });
    };

    /**
     * Creates the panel and overlays that will allow adding new pins
     * 
     * @param {button anchor to close the add pin mode} $anchor
     **/
    $axpin.createAddPinPanel = function ($anchor, htmlGenerator, eventBind) {
        var $addPinPanel = $('#addPinPanel');
        if (!$addPinPanel.length) {
            // targetPanel dimensions
            var zIndex = parseInt($clippingBounds.css('z-index'));
            //var zIndex = parseInt($targetPanel.css('z-index'));
            if (isNaN(zIndex)) zIndex = 0;

            var pinPanel = document.createElement('div');
            pinPanel.setAttribute('id', 'addPinPanel');

            $targetPanel.append(pinPanel);
            //$clippingBounds.append(pinPanel);

            addPinControlOverlay($('#addPinPanel'), $anchor);
            addPinTargetOverlay($('#addPinPanel'));
            bindAddPinPanelEvents(htmlGenerator, eventBind);

            // Add class to know which saved pins were originally expanded
            // Toggle all saved pins to be hidden
            $('.savedPin .pinIssueBubble:visible').parent().addClass('pinWasExpanded').click();
        } else if($addPinPanel.css('display') === 'none') {
            $addPinPanel.css('display',"");
        }
        $axure.messageCenter.postMessage('addPinModeToggle', true);
    };
})();